package com.example.empleemanagesystem.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import java.io.PrintWriter;

// 启用Spring Security的Web安全支持，这个注解会开启一系列Spring Security的Web相关的默认配置和功能
@EnableWebSecurity
// 标记为配置类，告知Spring框架这是一个用于配置各种Bean以及应用相关配置的类，Spring会自动扫描并处理该类中的配置逻辑
@Configuration
public class SecurityConfig {

    /**
     * 密码加密器
     * 此方法定义了一个Spring Bean，用于对用户密码进行加密处理，在存储密码以及后续验证密码时会用到加密后的形式。
     * 每次使用BCryptPasswordEncoder对同一个密码进行加密，得到的结果是不同的。
     * 这是因为 BCrypt 加密算法会在加密过程中使用随机的盐（salt）值。盐值是一个随机生成的字符串，它会和密码一起参与哈希运算。这种机制增加了密码的安全性，使得即使密码相同，每次加密后的哈希值也不同。
     * 但是，验证密码时，BCryptPasswordEncoder会从存储的密码哈希值中提取盐值，然后使用相同的盐值和密码进行哈希运算，最后将得到的结果与存储的哈希值进行比较，以确定密码是否正确。
     * @return PasswordEncoder实例，这里返回的是BCryptPasswordEncoder实例，它使用BCrypt加密算法来加密密码，BCrypt是一种安全可靠的单向哈希加密算法。
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 配置安全过滤链
     * 这个方法用于构建整个应用的安全过滤链，定义了哪些请求需要认证、授权以及各种与安全相关的规则，比如登录、注销等功能的配置。
     * @param http HttpSecurity对象
     * @return SecurityFilterChain实例，被Spring Security用于拦截和处理请求的安全验证。
     * @throws Exception 用于处理异常，调用这个方法的代码可以选择使用try-catch块来捕获并处理具体的异常（比如记录日志、给用户友好的提示等），也可以继续在自己的方法签名处也声明抛出异常，把处理的责任再往上传递，让更外层的调用者去处理。
     */
    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                        // 配置不需要认证的请求路径，也就是这些路径对应的请求可以被任意用户（无论是否登录）访问
                        .requestMatchers("/index", "/home").permitAll()
                        .requestMatchers("/attendance/list").permitAll()
                        // 配置需要特定角色的请求路径，只有具有指定角色（比如"HR"、"SYSTEM_ADMIN"等）的用户才能访问这些请求路径
                        .requestMatchers("/attendance/late").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/attendance/early").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/hello_sa").hasRole("SYSTEM_ADMIN")
                        .requestMatchers("/hello_sm").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/list").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/saveOrUpdate").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/delete/**").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/updateDepartment/**").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/updateBirthday/**").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/updateSalary/**").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/employee/updatePosition/**").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/salary/all").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/salary/calculateByName/**").hasAnyRole("HR","SYSTEM_ADMIN")
                        .requestMatchers("/user/view/**").hasRole("ADMIN")
                        .requestMatchers("/user/list").hasAuthority("user:list")
                        .requestMatchers("/verifycode").permitAll()
                        // 除上面明确配置过的可以匿名访问或者特定角色访问的请求之外，其余所有请求都要求用户登录后才能访问
                        .anyRequest().authenticated()
                )
                // 配置表单登录，允许所有用户访问登录页面，意味着未登录的用户可以访问登录页面进行登录操作，Spring Security会自动处理表单提交的登录逻辑
                .formLogin(AbstractAuthenticationFilterConfigurer::permitAll)
                // 配置注销功能，定义了注销相关的规则，比如注销的请求路径、注销成功后的处理逻辑等
                .logout(logout -> logout
                        .logoutUrl("/logout") // 注销请求路径，用户通过访问这个路径来发起注销操作
                        .logoutSuccessHandler((req, resp, authentication) -> { // 注销成功处理器，当注销操作成功后，会执行这里定义的逻辑
                            //设置响应的内容类型（Content-Type）头部信息,告诉浏览器按照 JSON 格式进行编码的，并且字符编码采用的是 UTF-8 编码格式。
                            resp.setContentType("application/json;charset=utf-8");
                            // 获取响应输出流，用于向客户端（浏览器）发送响应数据
                            PrintWriter out = resp.getWriter();
                            // 向客户端发送 JSON 格式的注销成功消息
                            out.write("{\"message\":\"logout success\"}");
                            //强制将之前写入到PrintWriter缓冲区中的数据立即发送到客户端
                            out.flush();
                        })
                        .permitAll() // 允许所有用户访问注销功能，无论是否登录都可以发起注销请求
                )
                // 禁用CSRF（跨站请求伪造）保护
                .csrf(AbstractHttpConfigurer::disable);
        return http.build(); // 构建SecurityFilterChain实例，将配置好的安全规则等信息整合到安全过滤链中，供Spring Security使用
    }
}